unit uMainExecute;

interface

uses
  Classes;
  
const
  HEAD_GUID: string = '{FF320F18-3BF7-43B0-A7D6-7E6F9368454A}';

type
  TCommonThread = class(TThread)
  private
    FThreadMethod: TThreadMethod;
  protected
    procedure Execute; override;
  public
    property ThreadMethod: TThreadMethod read FThreadMethod write FThreadMethod;
  end;

  TMainExecute = class
  private
    FSessionID: string;
    FUrl: string;
    FCmdWorkPath: string;
    FCmdLine: string;
    FBoImmeadRead: Boolean;
    FReadShellOutThread: TCommonThread;
    procedure OnBeforeHeaderSend(Sender: TObject;
      const Method: String; Headers: TStrings);
  private
    function GetWebUrl(var strResult: string; headers: array of string): Boolean;
    procedure HandleHttpResult(const data: string);
    procedure ReadShellOut;    
  public
    constructor Create;
    destructor Destroy; override;
    procedure Execute;
  end;

var
  G_MainExecute: TMainExecute;
implementation

uses
  idHttp, HttpProt, SysUtils, Windows, uGlobal;
{ TCommonThread }

procedure TCommonThread.Execute;
begin
  if Assigned(FThreadMethod) then
    FThreadMethod();
end;

{ TMainExecute }

constructor TMainExecute.Create;
begin
  inherited Create;
  FReadShellOutThread := TCommonThread.Create(True);
  FReadShellOutThread.ThreadMethod := ReadShellOut;
end;

destructor TMainExecute.Destroy;
begin
  FReadShellOutThread.Free;
  inherited;
end;

procedure TMainExecute.ReadShellOut;
var
  strData: string;
  syncTime: Word;
  LastRunTick: Cardinal;
begin
  syncTime := 100;
  LastRunTick := GetTickCount;
  try
    while True do
    begin
      if FBoImmeadRead or (GetTickCount-LastRunTick > syncTime) then
      begin
        LastRunTick := GetTickCount;
        if GetWebUrl(strData,
                    ['X-CMD:GetShellOut',
                     'X-SESSIONID:' + FSessionID,
                     'X-DATA:']) and (Length(strData)>0) then
        begin
          syncTime := 10;
          FBoImmeadRead := True;
          HandleHttpResult(strData);
        end else
        begin
          syncTime := 1000 * 5;
          FBoImmeadRead := False;
        end;
      end;
      Sleep(10);
    end;
    WriteLn('[exception ]ReadShellOut Exit');
  except
    on E: Exception do
    begin
      WriteLn('[exception ]ReadShellOut ' + E.Message);
    end;
  end;
end;

function TMainExecute.GetWebUrl(var strResult: string;
  headers: array of string): Boolean;
var
  i: Integer;
  idHttp: TIdHttp;
begin
  Result := True;
  strResult := '';
  idHttp := TIdHttp.Create(nil);
  try
    try
      idHttp.HandleRedirects := True;
      idHttp.ReadTimeout := 1000 * 30;
      for i := Low(headers) to High(headers) do
      begin
        idHttp.Request.CustomHeaders.Add(headers[i]);
      end;
      strResult := idHttp.Get(FUrl);
    except
      on E: Exception do
      begin
        Result := False;
      end;
    end;
  finally
    idHttp.Free;
  end;
end;

procedure TMainExecute.HandleHttpResult(const data: string);
var
  rawData: string;
begin
  if Pos(HEAD_GUID, data)>0 then
  begin
    rawData := Copy(data, Length(HEAD_GUID) + 1, Length(data) - Length(HEAD_GUID));
    rawData := Utf8Decode(Base64ToStr(rawData));
    Write(rawData);
  end;
end;

procedure TMainExecute.OnBeforeHeaderSend(Sender: TObject;
  const Method: String; Headers: TStrings);
begin
  Headers.Add('X-CMD:ExeShell');
  Headers.Add('X-SESSIONID:' + FSessionID);
  Headers.Add('X-DATA:' + StrToBase64(FCmdLine));
end;

procedure TMainExecute.Execute;
var
  strData: string;
begin
  if ParamCount<1 then
  begin
    Writeln('usuage: url');
    Exit;
  end;
  FUrl := ParamStr(1);
  if ParamCount>1 then
  begin
    FCmdWorkPath := ParamStr(1);
  end;

  if GetWebUrl(strData,
               ['X-CMD:CreateSession',
                'X-SESSIONID:0',
                'X-DATA:' + StrToBase64(Utf8Encode(FCmdWorkPath))]) then
  begin
    strData := Utf8Decode(Base64ToStr(strData));
    if Pos('|', strData)>0 then
    begin
      FSessionID := Copy(strData, 1, Pos('|', strData)-1);
      Writeln(Copy(strData, Pos('|', strData)+1, Length(strData) - Pos('|', strData) - 1));
    end else
    begin
      FSessionID := strData;
    end;
  end;
  FBoImmeadRead := True;
  FReadShellOutThread.Resume;
  while True do
  begin
    ReadLn(FCmdLine);
    if SameText(Trim(FCmdLine), 'quit') then
    begin
      ExitProcess(0);
      Exit;
    end;

    if GetWebUrl(strData,
                 ['X-CMD:ExeShell',
                  'X-SESSIONID:' + FSessionID,
                  'X-DATA:' + StrToBase64(FCmdLine+#13#10)]) then
      HandleHttpResult(strData);
    FBoImmeadRead := True;
  end;
end;

end.
